﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Scrabble.Model
{
    public abstract class Lexicon : ILexicon
    {
        private readonly List<string> _allWords;

        protected Lexicon(IEnumerable<string> allWords)
        {
            _allWords = allWords.ToList();
        }

        public IEnumerable<string> AllWords
        {
            get { return _allWords; }
        }

        IEnumerable<string> ILexicon.AllWordsComposedOf(string letters)
        {
            // From Eric Lipperts blog post:
            // http://blogs.msdn.com/b/ericlippert/archive/2009/02/04/a-nasality-talisman-for-the-sultana-analyst.aspx

            var searchKeys = ReplaceBlanks(letters).Select(Canonicalize).Distinct().ToArray();

            return AllWords
                .Where(word => word.Length == letters.Length)
                .Where(word => searchKeys.Contains(Canonicalize(word)));
        }

        IEnumerable<string> ILexicon.AllWordsStartingWith(string prefix)
        {
            var expression = prefix.Contains(Tile.BlankChar)
                ? new Regex("^" + prefix.Replace(Tile.BlankChar, '.'))
                : null;

            var isMatch = expression == null
                ? word => word.StartsWith(prefix)
                : new Predicate<string>(expression.IsMatch);

            return AllWords
                .Where(word => word.Length >= prefix.Length)
                .Where(word => isMatch(word));
        }

        bool ILexicon.Contains(string word)
        {
            return AllWords.Contains(word);
        }

        private static IEnumerable<string> ReplaceBlanks(string letters)
        {
            var indexOfBlank = letters.IndexOf(Tile.BlankChar);

            return indexOfBlank == -1
                ? new[] { letters }
                : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                    .Select(ch => letters.Substring(0, indexOfBlank) + ch.ToString() + letters.Substring(indexOfBlank + 1))
                    .SelectMany(ReplaceBlanks);
        }

        private static string Canonicalize(string letters)
        {
            var chars = letters.ToUpperInvariant().ToCharArray();
            Array.Sort(chars);
            return new string(chars);
        }
    }
}